package com.byron.media.server.handlers;


import com.byron.media.server.config.MediaServerConfig;
import com.byron.media.server.model.MediaData;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.NetUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.net.*;
import java.util.Enumeration;
import java.util.concurrent.BlockingQueue;

@Slf4j
public class UdpMediaDataSender extends Thread {

    private long frameIndex = 0l;

//    private DatagramSocket udpSocket;

    private boolean running = true;

    private BlockingQueue<MediaData> frameQueue;

    //    private List<FrameInfo> frameCache;
    private File testFile;

    private String broadcastAddress;

    private int seqNum = 0;

    private int port;

    private byte[] tempData;        // 发送缓存

    private int tempLength;         // 发送缓存长度

    private NioDatagramChannel channel;

    private InetSocketAddress groupAddress;

    public UdpMediaDataSender(String broadcastAddress, int port) {
        this.broadcastAddress = broadcastAddress;
        this.port = port;
//        this.testFile = new File("C://temp/" + System.currentTimeMillis() + "_" + broadcastAddress + ".h264");
//        try {
//            this.testFile.createNewFile();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        frameCache = new ArrayList<>();
//        frameQueue = new LinkedBlockingQueue<>(1024);
//        tempData = new byte[1024 * 1024 * 2];
//        tempLength = 0;
    }


    @Override
    public void run() {

//        try {
        // 组播用以下的方式
//            if(broadcastAddress.startsWith("192.168")){
//                udpSocket = new DatagramSocket();
//                udpSocket.setBroadcast(true);
//            } else {
//                udpSocket = new MulticastSocket(port);
//                udpSocket.setBroadcast(true);
//                ((MulticastSocket)udpSocket).joinGroup(InetAddress.getByName(broadcastAddress));
//            }
//        Bootstrap b = new Bootstrap();
        EventLoopGroup group = new NioEventLoopGroup();
        NetworkInterface ni = NetUtil.LOOPBACK_IF;
//        NetworkInterface ni = null;
//        try {
//            ni = NetworkInterface.getNetworkInterfaces().nextElement();
//        } catch (SocketException e) {
//            e.printStackTrace();
//        }


        try {
            Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
            while(nis.hasMoreElements()){
                NetworkInterface n = nis.nextElement();
                if(MediaServerConfig.getInstance().isLog()){
                    log.info(n.getDisplayName() + " : " + n.getName() +
                            " : " + n.isLoopback() +
                            " : " + n.supportsMulticast() +
                            " : " + n.isVirtual());
                }
            }
        } catch (SocketException e) {
            e.printStackTrace();
        }
        groupAddress = new InetSocketAddress(broadcastAddress, port);
        try {
//            b.group(group)
//                    .channel(NioDatagramChannel.class)
////                    .option(ChannelOption.IP_MULTICAST_ADDR, InetAddress.getByName(this.broadcastAddress))
////                    .option(ChannelOption.IP_MULTICAST_IF, NetUtil.LOOPBACK_IF)
//                    .option(ChannelOption.SO_REUSEADDR, true)
//                    .option(ChannelOption.SO_BROADCAST, true)
//                    .handler(new UdpServerHandler());

            Enumeration<InetAddress> addresses = ni.getInetAddresses();
            InetAddress localAddress = null;
            while (addresses.hasMoreElements()) {
                InetAddress address = addresses.nextElement();
                if (address instanceof Inet4Address){
                    localAddress = address;
                }
            }
            System.out.println(localAddress);

            Bootstrap bootstrap = new Bootstrap();
            //设置NioDatagramChannel
            bootstrap.group(group).channel(NioDatagramChannel.class)
                    .localAddress(localAddress, groupAddress.getPort())
                    //设置Option 组播
                    .option(ChannelOption.IP_MULTICAST_IF, ni)
                    //设置Option 地址
                    .option(ChannelOption.IP_MULTICAST_ADDR, localAddress)
                    //设置地址
                    .option(ChannelOption.SO_REUSEADDR, true)
                    .handler(new ChannelInitializer<NioDatagramChannel>() {
                        @Override
                        public void initChannel(NioDatagramChannel ch) throws Exception {
                            ChannelPipeline addLast = ch.pipeline().addLast();
                            addLast.addLast(new SimpleChannelInboundHandler<io.netty.channel.socket.DatagramPacket>() {//接收信息处理

                                @Override
                                protected void channelRead0(ChannelHandlerContext ctx, io.netty.channel.socket.DatagramPacket msg) throws Exception {
                                    // 打印一句话
//                                    System.out.println(msg.sender()+" >>> "+new String(msg.content().toString(CharsetUtil.UTF_8)));
                                }
                            });
                        }
                    });

            //获取NioDatagramChannel
            channel = (NioDatagramChannel) bootstrap.bind(port).sync().channel();
            //加入组
//            channel.joinGroup(groupAddress, ni).sync();
            //关闭Channel
            channel.closeFuture().await();

        } catch (Exception e) {
            log.error(e.toString(), e);

        } finally {
//            if(channel != null){
//                try {
//                    channel.leaveGroup(groupAddress, ni).sync();
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//            }
            group.shutdownGracefully();
        }
//        try {
//            channel = (NioDatagramChannel) b.bind(port).sync().channel();
////            channel.joinGroup(InetSocketAddress.createUnresolved(broadcastAddress, port), NetUtil.LOOPBACK_IF).sync();
//            channel.joinGroup(InetAddress.getByName(broadcastAddress)).sync();
//            channel.closeFuture().await();
////                b.bind(port).sync().channel().closeFuture().await();
//        } catch (InterruptedException | UnknownHostException e) {
//            e.printStackTrace();
//        } finally {
//            if(channel != null){
//                try {
//                    channel.leaveGroup(InetAddress.getByName(broadcastAddress)).sync();
//                } catch (InterruptedException | UnknownHostException e) {
//                    e.printStackTrace();
//                }
//            }
//            group.shutdownGracefully();
//        }

//            while(running){
//                MediaData dataInfo = null;
//                try {
//                    dataInfo = frameQueue.take();
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                    if(!running){
//                        return;
//                    }
//                }
//                if(!running){
//                    break;
//                }
//                if(dataInfo != null){
//                    // 如果是关键帧则缓存起来
//                    byte[] data = new byte[dataInfo.getLength() + 15];
//                    data[0] = '$';
//                    data[1] = 0;
//                    data[2] = dataInfo.getType();
//                    CommonUlit.putLongMina(data, dataInfo.getID(), 3);
//                    CommonUlit.putInt(data, dataInfo.getLength(), 11);
//                    System.arraycopy(dataInfo.getData(), 0, data, 15, dataInfo.getLength());
//                    DatagramPacket packet = new DatagramPacket(data, 0, data.length, InetAddress.getByName(broadcastAddress), port);
//                    udpSocket.send(packet);
//                }
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        } finally {
//            if(udpSocket != null){
//                try {
//                    if(udpSocket instanceof MulticastSocket){
//                        ((MulticastSocket)udpSocket).leaveGroup(InetAddress.getByName(broadcastAddress));
//                    }
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//                udpSocket.close();
//                udpSocket = null;
//            }
//        }
    }



    public void pushFrameInfo(MediaData dataInfo){
//        try {
//            frameQueue.put(dataInfo);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

        if(dataInfo != null && channel != null){
            // 如果是关键帧则缓存起来
            ByteBuf buf = channel.alloc().buffer(dataInfo.getLength() + 4);
            buf.writeByte((byte)'$');   // $ 开始符
            buf.writeByte(dataInfo.getChannel());     // 通道号
            buf.writeByte(dataInfo.getType());     // 类型
            buf.writeLong(dataInfo.getID());           // 单元号
            buf.writeInt(dataInfo.getLength());  // 数据长度
            buf.writeBytes(dataInfo.getData(), 0, dataInfo.getLength());       // 数据
            io.netty.channel.socket.DatagramPacket packet = new DatagramPacket(buf, groupAddress);
            channel.writeAndFlush(packet, channel.newPromise());
        }
    }


    /**
     * 关闭线程
     */
    public void close(){
//        running = false;
//        try {
//            frameQueue.put(new MediaData((byte)0, (byte)0, 0L, new byte[1], 0));
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        if(channel != null){
            channel.close();
        }
    }

}

